#!/usr/bin/env python

import utiles
import servers
import MySQLdb
import MySQLdb.cursors
import string
from decimal import Decimal
import sys
import os

host=servers.host
user=servers.user
passwd=servers.passwd
db_name=servers.db_name

miBD=MySQLdb.connect(host=host,user=user,passwd=passwd,db=db_name)
miCursor=miBD.cursor()
miBDDict=MySQLdb.connect(host=host,user=user,passwd=passwd,db=db_name, cursorclass=MySQLdb.cursors.DictCursor)
miCursorDict=miBDDict.cursor()

lineaGuiones = "-"*107
lineaRenglon = "%-8s %-20s %-15s %10s %10s  %1s %11s %11s %11s"

def ordenCruda(consultaSQL):
    miCursor.execute(consultaSQL)
    auxiliar = miCursor.fetchall()
    return auxiliar

def ordenCrudaDict(consultaSQL, previo="SELECT 1"):
    miCursorDict.execute(previo)
    miCursorDict.execute(consultaSQL)
    auxiliar = miCursorDict.fetchall()    
    return auxiliar

def datosCliente(idcliente):
    sql = "SELECT nombre,telefono FROM cliente WHERE idcliente = '%s'" % (idcliente)
    datos = ordenCruda(sql)
    if datos != ():
        datos = (datos[0][0], datos[0][1])
    else:
        datos = None
    return datos

def getSucursalActiva():
    sql = "SELECT idsucursal FROM sucursales WHERE activa = 1"
    sucursal = ordenCrudaDict(sql)
    if sucursal == () or sucursal == None: return "0"
    sucursal = sucursal[0]['idsucursal']
    return sucursal

# FUNCIONES SIMPLES DE FECHA

def invertirFecha(fecha):
    #recibe     STRING "YYYY-mm-dd"
    #devuelve   STRING "dd-mm-yy"   (recorta los digitos del anio)
    fecha = str(fecha)
    return fecha[8:10]+"-"+fecha[5:7]+"-"+fecha[2:4]

def fechaInversa(fecha):
    #recibe     STRING  "dd-mm-YYYY"
    #devuelve   STRING  "YYYY-mm-dd"
    return fecha[-4:]+"-"+fecha[3:5]+"-"+fecha[:2]

def getfechaToday():
    #devuelve   STRING  "dd-mm-yy"  (fecha actual del sistema)
    from datetime import date
    now = date.today()
    return str(now.strftime("%d-%m-%y"))

def getfechaTodayCompleta():
    #devuelve   STRING  "dd-mm-YYYY"  (fecha actual del sistema)
    from datetime import date
    now = date.today()
    return str(now.strftime("%d-%m-%Y"))

def getfechaInverse():
    #devuelve   STRING  "YYYY-mm-dd"    (fecha actual del sistema)
    from datetime import date
    now = date.today()
    return now.strftime("%Y-%m-%d")

def getfechaInverseTime():
    #devuelve   STRING  "YYYY-mm-dd HH:MM:SS"
    import time
    return time.strftime("%Y-%m-%d %H:%M-%S")

def convertirFecha(fecha):
    #recibe     DATETIME
    #devuelve   STRING      "dd-mm-YYYY"
    return fecha.strftime("%d-%m-%Y")

def strToDate(fecha):
    #recibe     STRING      "YYYY-mm-dd"
    #devuelve   DATETIME    "YYYY-mm-dd"
    from datetime import date
    return date(*map(int,fecha.split("-")))
            
def diaSemana(fecha):
    #se le pasa una fecha datetime y devuelve el numero de dia de la semana
    #1 Lunes, 2 Martes, 3, Miercoles, 4 Jueves, 5, Viernes, 6 Sabado, 7 Domingo
    from datetime import date
    #from datetime import timedelta
    return fecha.isoweekday()

# *******************************************************************

def getfechaDiaLaboral(fecha,valor):
    #fecha es tipo datetime y devuelve ese mismo tipo de datos
    #valor es positivo o negativo, segun sume o reste dias. fecha+valor=nuevaFecha
    from datetime import timedelta
    consultaSQL = "SELECT fecha FROM feriados"
    resultado = ordenCruda(consultaSQL)
    feriado = [] 
    for dia in resultado:
        feriado.append(dia[0])
    dif = timedelta(days=valor)
    fecha = fecha + dif
    while (fecha in feriado) or ((diaSemana(fecha) == 7)):
        fecha = fecha + dif
    return fecha

def sumarDiasAFecha(fecha,valor):
    #fecha es tipo datetime y devuelve ese mismo tipo de datos
    #valor es positivo o negativo, segun sume o reste dias. fecha+valor=nuevaFecha
    from datetime import timedelta
    dif = timedelta(days=valor)
    fecha = fecha + dif
    return fecha

def getfechaDiaLaboralAnterior(fecha):
    #recibe y devuelve un valor datetime
    return getfechaDiaLaboral(fecha,-1)

def getfechaDiaLaboralSiguiente(fecha):
    #recibe y devuelve un valor datetime
    return getfechaDiaLaboral(fecha,1)

def sumarDiasLaboralesAFecha(valor):
    aux = strToDate(getfechaInverse())
    if valor == 0:
        return aux
    if valor < 0:
        multiplicador = -1
    else:
        multiplicador = 1
    valor = abs(valor)
    for i in xrange(valor):
        aux = getfechaDiaLaboral(aux,multiplicador)
    return aux

def isDiaNoLaboral(fecha):
    consultaSQL = "SELECT fecha FROM feriados"
    resultado = ordenCruda(consultaSQL)
    feriado = []
    for dia in resultado:
        feriado.append(dia[0])
    if (fecha in feriado) or ((diaSemana(fecha) == 7)):
        return True
    else:
        return False

def isDiaLaboral(fecha):
    return not isDiaNoLaboral(fecha)
    
def getDaysExtreme():
    from calendar import monthrange
    from datetime import datetime
    from time import strptime
    now = date.today()
    mes = int(now.strftime("%m"))
    anio = int(now.strftime("%Y"))
    primerDia = '01'+str(now.strftime("-%m-%Y"))
    ultimoDia = str(monthrange(int(anio),int(mes))[1])+str(now.strftime("-%m-%Y"))
    primerDia = datetime(*strptime(primerDia, "%d-%m-%Y")[0:5])
    ultimoDia = datetime(*strptime(ultimoDia, "%d-%m-%Y")[0:5])
    return [primerDia, ultimoDia]
    
def isFechaDelMesActual(fecha):
    #fecha debe venir como datetime y en formato Y-m-d
    return getfechaInverse()[:7] == str(fecha)[:7]

def isFechaDelMesAnterior(fecha):
    #fecha debe venir como datetime y en formato Y-m-d
    return getfechaInverse()[:7] > str(fecha)[:7]

def getFechaLaboralMesSiguiente(fecha):
    from datetime import timedelta
    dif = timedelta(days=30)
    fecha = fecha + dif
    if isDiaNoLaboral(fecha):
        return getfechaDiaLaboralSiguiente(fecha)
    else:
        return fecha

# *****************************************************************************

def listaClientes(sucursal = None, idcliente = None):
    if sucursal == None:
        if idcliente != None:
            sql = "SELECT idcliente,nombre,telefono FROM cliente WHERE idcliente='%s'" % (idcliente)
        else:
            sql = "SELECT idcliente,nombre,telefono FROM cliente"
    else:
        sql = "SELECT idcliente,nombre,telefono FROM cliente WHERE idcliente LIKE '%s-" % (sucursal)
        sql = sql + "%'"

    listaClientes = ordenCruda(sql)
    if listaClientes == (): listaClientes = None
    return listaClientes

def obtenerDeuda90(idcliente,dias):
    #deuda vencida de mas de 90 dias
    sql = "SELECT sum(debe) FROM vtas_detalle WHERE idcliente='%s' AND fecha<'%s'" % (idcliente,dias[2])
    deuda = ordenCruda(sql)
    if deuda == (): 
        deuda = Decimal("0.00")
    elif deuda[0][0] == None:
        deuda = Decimal("0.00")
    else:
        deuda = deuda[0][0]
    return deuda

def obtenerDeuda60(idcliente,dias):
    #deuda vencida de mas de 60 dias (incluye la de 90)
    sql = "SELECT sum(debe) FROM vtas_detalle WHERE idcliente='%s' AND fecha<'%s'" % (idcliente,dias[1])
    deuda = ordenCruda(sql)
    if deuda == (): 
        deuda = Decimal("0.00")
    elif deuda[0][0] == None:
        deuda = Decimal("0.00")
    else:
        deuda = deuda[0][0]
    return deuda

def obtenerDeuda30(idcliente,dias):
    #deuda vencida de mas de 30 dias (incluye la de 60 y 90 dias)
    sql = "SELECT sum(debe) FROM vtas_detalle WHERE idcliente='%s' AND fecha<'%s'" % (idcliente,dias[0])
    deuda = ordenCruda(sql)
    if deuda == (): 
        deuda = Decimal("0.00")
    elif deuda[0][0] == None:
        deuda = Decimal("0.00")
    else:
        deuda = deuda[0][0]
    return deuda

def obtenerDeuda(idcliente):
    #deuda actual
    sql = "SELECT sum(debe) FROM vtas_detalle WHERE idcliente='%s'"
    deuda = ordenCruda(sql)
    if deuda == (): 
        deuda = Decimal("0.00")
    elif deuda[0][0] == None:
        deuda = Decimal("0.00")
    else:
        deuda = deuda[0][0]
    return deuda

def obtenerDeudaActual(idcliente,deuda30,deuda60,deuda90):
    deuda = obtenerDeuda(idcliente)

    #deuda incluye deuda30, deuda60 y deuda90
    deuda = deuda - deuda30 - deuda60 - deuda90
    return deuda

def obtenerPagos(idcliente):
    sql = "SELECT sum(haber) FROM vtas_detalle WHERE idcliente='%s'" % (idcliente)
    pago = ordenCruda(sql)
    if pago == (): 
        pago = Decimal("0.00")
    elif pago[0][0] == None:
        pago = Decimal("0.00")
    else:
        pago = pago[0][0]
    return pago
 
def determinarDias():
    #considera todos los dias
    fechaHoy = strToDate(getfechaInverse())
    fecha30 = sumarDiasAFecha(fechaHoy,-30)
    fecha60 = sumarDiasAFecha(fechaHoy,-60)
    fecha90 = sumarDiasAFecha(fechaHoy,-90)
    return [fecha30,fecha60,fecha90] #datetime

def printEncabezado():
    #encabezado en pantalla
    print
    print lineaRenglon % ("ID","NOMBRE","TELEFONO","Ult.Pago","","","30 dias","60 dias","90 dias+")
    print lineaGuiones

def printPie(totales):
    print lineaGuiones
    print lineaRenglon % ("","Subtotales","","","","|",totales[0],totales[1],totales[2])
    total = totales[0]+totales[1]+totales[2]
    print lineaRenglon % ("TOTAL","","","","","|","","",total)

def calcularDeuda(idcliente,dias):
    deuda30 = obtenerDeuda30(idcliente,dias)
    deuda60 = obtenerDeuda60(idcliente,dias)
    deuda90 = obtenerDeuda90(idcliente,dias)

    if deuda30 == Decimal("0.00") and deuda60 == Decimal("0.00") and deuda90 == Decimal("0.00"):
        return [Decimal("0.00"),Decimal("0.00"),Decimal("0.00")]

    #deuda 30 incluye deuda en 60 y 90
    deuda30 = deuda30 - deuda60 - deuda90
    if deuda30 < Decimal("0.00"): deuda30 = Decimal("0.00")
    #deuda 60 incluye deuda en 90
    deuda60 = deuda60 - deuda90
    if deuda60 < Decimal("0.00"): deuda60 = Decimal("0.00")

    if deuda30 > Decimal("0.00") or deuda60 > Decimal("0.00") or deuda90 > Decimal("0.00"):
        #ahora tomo el total de pagos realizados y voy descontando de la deuda mas vieja
        pago = obtenerPagos(idcliente)

        if pago > Decimal("0.00"):
            deuda90 = deuda90 - pago
            pago = pago - deuda90
            if deuda90 < Decimal("0.00"):
                pago = abs(deuda90)
                deuda90 = Decimal("0.00")

        if pago > Decimal("0.00"):
            deuda60 = deuda60 - pago
            pago = pago - deuda60
            if deuda60 < Decimal("0.00"):
                pago = abs(deuda60)
                deuda60 = Decimal("0.00")

        if pago > Decimal("0.00"):
            deuda30 = deuda30 - pago
            pago = pago - deuda30
            if deuda30 < Decimal("0.00"):
                pago = abs(deuda30)
                deuda30 = Decimal("0.00")
                
    return [deuda30,deuda60,deuda90]

def fechaUltimoPago(idcliente):
    sql = "SELECT fecha,haber FROM vtas_detalle WHERE idcliente='%s' AND haber>'0.00' ORDER BY fecha DESC" % (idcliente)
    fecha = ordenCrudaDict(sql)
    if fecha == (): return ""
    fecha = fecha[0]['fecha']
    fecha = convertirFecha(fecha)
    fecha = fecha[:6]+fecha[-2:]
    return fecha
    
# *****************************************************************************

if __name__ == '__main__':

    os.system("cls")

    if len(sys.argv) < 2:
        sucursal = str(getSucursalActiva())
        listaClientes = listaClientes(sucursal,None)
    else:
        if len(sys.argv[1]) == 1:
            sucursal = str(sys.argv[1])
            listaClientes = listaClientes(sucursal,None)
        else:
            listaClientes = listaClientes(None,sys.argv[1])
            if listaClientes == None:
                print "Error en parametros o cliente inexistente. Verifique"
                sys.exit()

    dias = determinarDias()

    totales = [Decimal("0.00"),Decimal("0.00"),Decimal("0.00")]

    printEncabezado()

    #detalle de deuda
    for cliente in listaClientes:
        idcliente = cliente[0]
        nombre = cliente[1]
        telefono = cliente[2]

        deudas = calcularDeuda(idcliente,dias)
        if deudas == [Decimal("0.00"),Decimal("0.00"),Decimal("0.00")]:
            continue
        
        deuda30 = deudas[0]
        deuda60 = deudas[1]
        deuda90 = deudas[2]
        
        if deuda30 > Decimal("0.00") or deuda60 > Decimal("0.00") or deuda90 > Decimal("0.00"):
            fecha = fechaUltimoPago(idcliente)
            print lineaRenglon % (idcliente[0:8],nombre[0:20],telefono[0:15],fecha,"","|",deuda30,deuda60,deuda90)
            print lineaGuiones
        
            totales[0] += deuda30
            totales[1] += deuda60
            totales[2] += deuda90
  
    printPie(totales)

    miCursor.close()
    miBD.close()
